//
// Created by HP on 2022/1/31.
//

#include "tmc_window.h"

// std
#include <cassert>

namespace tmc {

    TmcWindow::TmcWindow(int w, int h, std::string name): width{w}, height{h}, name{std::move(name)} {
        initWindow();
    }

    TmcWindow::~TmcWindow() {
        glfwDestroyWindow(window);
        glfwTerminate();
    }

    void TmcWindow::initWindow() {
        glfwInit();
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

        // glfw window creation
        window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr);
        assert(window != nullptr && "Failed to create GLFW window");
        glfwMakeContextCurrent(window);
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        glfwSetWindowUserPointer(window, this);
        glfwSetFramebufferSizeCallback(window, framebufferResizedCallback);
        glfwSetCursorEnterCallback(window, cursorEnterCallback);
        glfwSetMouseButtonCallback(window, mouseButtonInputCallback);
        glfwSetKeyCallback(window, keyboardInputCallback);
    }

    void TmcWindow::framebufferResizedCallback(GLFWwindow *window, int width, int height) {
        auto lveWindow = reinterpret_cast<TmcWindow * >(glfwGetWindowUserPointer(window));
        lveWindow->framebufferResized = true;
        lveWindow->width = width;
        lveWindow->height = height;
        glViewport(0, 0, width, height);
        glfwSwapBuffers(window);
    }

    void TmcWindow::cursorEnterCallback(GLFWwindow *window, int entered) {
        auto* lveWindowPtr = reinterpret_cast<TmcWindow*>(glfwGetWindowUserPointer(window));
        lveWindowPtr->isEnter = entered;
    }

    void TmcWindow::getCursorPos(double *xPos, double *yPos) {
        if (isEnter) {
            glfwGetCursorPos(window, xPos, yPos);
            *xPos /= static_cast<double>(width);
            *xPos *= 2.f;
            *xPos -= 1.f;
            *yPos /= static_cast<double>(height);
            *yPos *= 2.f;
            *yPos -= 1.f;
        } else {
            *xPos = 0.f;
            *yPos = 0.f;
        }
    }

    void TmcWindow::mouseButtonInputCallback(GLFWwindow *window, int button, int action, [[maybe_unused]] int mods) {
        auto* lveWindowPtr = reinterpret_cast<TmcWindow*>(glfwGetWindowUserPointer(window));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS && lveWindowPtr->isEnter) {
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN | GLFW_CURSOR_DISABLED);
        }
    }

    void TmcWindow::keyboardInputCallback(GLFWwindow *window, int key,
                                          [[maybe_unused]] int scancode, int action, [[maybe_unused]] int mods) {
        auto* lveWindowPtr = reinterpret_cast<TmcWindow*>(glfwGetWindowUserPointer(window));
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
            lveWindowPtr->isEnter = false;
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        }
    }
}